package com.lid.lib;

import static ohos.agp.components.AttrHelper.getDensity;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.StackLayout;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.app.Context;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * LabelView;
 *
 * @since 2021-04-13
 */
public class LabelView extends Text {
    private static final int INT = 2;
    private static final float DENSITY = 0.5f;
    private static final int COLOR = 0x00FFFFFF;
    private static final int SIZE = 12;
    private static final int LEFT_TOP = -45;
    private static final int RIGHT_TOP = 45;
    private static final int BLUE = 255;
    private static final int LABELVIEWCONTAINERID = -1;
    private static final double FLOAT = 1.414;
    private static AtomicInteger sNextGeneratedId = new AtomicInteger(1);
    private double offsetx;
    private double offsety;
    private double anchorx;
    private double anchory;
    private double angel;
    private int labelViewContainerId;

    /**
     * Gravity;
     *
     * @since 2021-04-13
     */
    public enum Gravity {
        /**
         * LEFT_TOP, RIGHT_TOP
         *
         */
        LEFT_TOP, RIGHT_TOP
    }

    /**
     * LabelView
     *
     * @param context
     */
    public LabelView(Context context) {
        this(context, null);
    }

    /**
     * LabelView
     *
     * @param context
     * @param attrs
     */
    public LabelView(Context context, AttrSet attrs) {
        this(context, attrs, 0);
    }

    /**
     * LabelView
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public LabelView(Context context, AttrSet attrs, int defStyle) {
        super(context, attrs, String.valueOf(defStyle));
        init();
    }

    private void init() {
        if (!(getLayoutConfig() instanceof ComponentContainer.LayoutConfig)) {
            DependentLayout.LayoutConfig layoutParams =
                    new DependentLayout.LayoutConfig(
                            ComponentContainer.LayoutConfig.MATCH_PARENT,
                            ComponentContainer.LayoutConfig.MATCH_CONTENT);
            setLayoutConfig(layoutParams);
        }
        labelViewContainerId = LABELVIEWCONTAINERID;
        setForegroundGravity(Component.DRAG_HORIZONTAL_VERTICAL);
        setTextColor(Color.WHITE);
        setFont(Font.DEFAULT_BOLD);
        setTextSize(SIZE, TextSizeType.VP);

        ShapeElement shapeElement = new ShapeElement();
        shapeElement.setRgbColor(new RgbColor(0, 0, BLUE));
        setBackground(shapeElement);
    }

    /**
     * setTargetView
     *
     * @param target
     * @param distance
     * @param gravity
     */
    public void setTargetView(Component target, int distance, Gravity gravity) {
        if (!replaceLayout(target)) {
            return;
        }
    }

    /**
     * setTargetViewInBaseAdapter
     *
     * @param target
     * @param targetWidth
     * @param distance
     * @param gravity
     */
    public void setTargetViewInBaseAdapter(Component target, float targetWidth, int distance, Gravity gravity) {
        if (!replaceLayout(target)) {
            return;
        }
        calcOffset(dip2Px(getContext(), (float) targetWidth), distance, gravity, (float) targetWidth, true);
    }

    /**
     * remove
     *
     */
    public void remove() {
        if (getComponentParent() == null || labelViewContainerId == LABELVIEWCONTAINERID) {
            return;
        }

        ComponentContainer frameContainer = (ComponentContainer) getComponentParent();
        assert frameContainer.getChildCount() == INT;
        Component target = frameContainer.getComponentAt(0);

        ComponentContainer parentContainer = (ComponentContainer) frameContainer.getComponentParent();
        int groupIndex = parentContainer.getChildIndex(frameContainer);
        if (frameContainer.getComponentParent() instanceof DependentLayout) {
            for (int i = 0; i < parentContainer.getChildCount(); i++) {
                if (i == groupIndex) {
                    continue;
                }
                Component view = parentContainer.getComponentAt(i);
                DependentLayout.LayoutConfig para = (DependentLayout.LayoutConfig) view.getLayoutConfig();
                for (int j = 0; j < para.getRules().length; j++) {
                    if (para.getRules()[j] == labelViewContainerId) {
                        para.getRules()[j] = target.getId();
                    }
                }
                view.setLayoutConfig(para);
            }
        }

        ComponentContainer.LayoutConfig frameLayoutParam = frameContainer.getLayoutConfig();
        target.setLayoutConfig(frameLayoutParam);
        parentContainer.removeComponentAt(groupIndex);
        frameContainer.removeComponent(target);
        frameContainer.removeComponent(this);
        parentContainer.addComponent(target, groupIndex);
        labelViewContainerId = LABELVIEWCONTAINERID;
    }

    private boolean replaceLayout(Component target) {
        if (getComponentParent() != null || target == null || target.getComponentParent()
                == null || labelViewContainerId != LABELVIEWCONTAINERID) {
            return false;
        }

        ComponentContainer parentContainer = (ComponentContainer) target.getComponentParent();

        if (target.getComponentParent() instanceof StackLayout) {
            ((StackLayout) target.getComponentParent()).addComponent(this);
        } else if (target.getComponentParent() instanceof ComponentContainer) {
            int groupIndex = parentContainer.getChildIndex(target);
            labelViewContainerId = generateViewId();

            // relativeLayout need copy rule
            if (target.getComponentParent() instanceof DependentLayout) {
                for (int i = 0; i < parentContainer.getChildCount(); i++) {
                    if (i == groupIndex) {
                        continue;
                    }
                    Component view = parentContainer.getComponentAt(i);
                    DependentLayout.LayoutConfig para = (DependentLayout.LayoutConfig) view.getLayoutConfig();
                    for (int j = 0; j < para.getRules().length; j++) {
                        if (para.getRules()[j] == target.getId()) {
                            para.getRules()[j] = labelViewContainerId;
                        }
                    }
                    view.setLayoutConfig(para);
                }
            }
            parentContainer.removeComponent(target);

            // new dummy layout
            StackLayout labelViewContainer = new StackLayout(getContext());
            ComponentContainer.LayoutConfig targetLayoutParam = target.getLayoutConfig();
            labelViewContainer.setLayoutConfig(targetLayoutParam);
            target.setLayoutConfig(new ComponentContainer.LayoutConfig(
                    ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_PARENT));

            // add target and label in dummy layout
            labelViewContainer.addComponent(target);
            labelViewContainer.addComponent(this);
            labelViewContainer.setId(labelViewContainerId);

            // add dummy layout in parent container
            parentContainer.addComponent(labelViewContainer, groupIndex, targetLayoutParam);
        }
        return true;
    }

    private void calcOffset(double labelWidth, int distance, Gravity gravity, double targetWidth, boolean isDp) {
        int dd = dip2Px(getContext(), distance);
        int tw = isDp ? dip2Px(getContext(), (float) targetWidth) : (int) targetWidth;

        float edge = (float) ((labelWidth - INT * dd) / (INT * FLOAT));
        if (gravity == Gravity.LEFT_TOP) {
            anchorx = -edge;
            offsetx = anchorx;
            angel = -LEFT_TOP;
        } else if (gravity == Gravity.RIGHT_TOP) {
            offsetx = tw + edge - labelWidth;
            anchorx = tw + edge;
            angel = RIGHT_TOP;
        }

        anchory = (float) (FLOAT * dd + edge);
        offsety = anchory;
    }

    /**
     * dip2Px
     *
     * @param context
     * @param fp
     * @return dip2Px
     */
    public static int dip2Px(final Context context, final float fp) {
        return AttrHelper.fp2px(fp, getDensity(context) + DENSITY);
    }

    /**
     * generateViewId
     *
     * @return int
     */
    public static int generateViewId() {
        for (; ; ) {
            final int result = sNextGeneratedId.get();
            int newValue = result + 1;
            if (newValue > COLOR) {
                newValue = 1;
            }
            if (sNextGeneratedId.compareAndSet(result, newValue)) {
                return result;
            }
        }
    }
}
